/**
 * 20220304:[require] to [import]...jordia
 */
import {EventDispatcher} from 'three';
import Utils from '../general/utils';

/**
 * 调试标记变量
 * @type {boolean}
 */
let debug=false;

/**
 * 常数
 * @type {number}
 */
const EPSILON=1e-12;
const EULER_NUMBER=2.718281828459;
const MAX_FLOAT=1e30;
const PI=Math.PI;
const HALF_PI=Math.PI*0.5;

/**
 * 字符串对比函数
 * @param a
 * @param b
 * @returns {boolean}
 */
const strCompare=(a,b)=>{
    return a===b;
};

/**
 * 正切
 * @param radian
 * @returns {number}
 */
const tan=(radian)=> {
    if (radian / HALF_PI % 1 ===0 && radian / PI % 1!==0) {
        console.warn('over range for math method:tan!');
        return MAX_FLOAT;
    } else {
        return Math.tan(radian);
    }
};

/**
 * 余切
 * @param radian
 * @returns {number}
 */
const cot=(radian)=>{
    let r=tan(radian);
    if(r===0){
        console.warn('over range for math method:cot!');
        return MAX_FLOAT;
    }
    return 1/r;
};

/**
 * 正割
 * @param radian
 * @returns {number}
 */
const sec=(radian)=> {
    let r=Math.cos(radian);
    if(r===0){
        console.warn('over range for math method:sec!');
        return MAX_FLOAT;
    }
    return 1 / r;
};

/**
 * 余割
 * @param radian
 * @returns {number}
 */
const csc=(radian)=> {
    let r=Math.sin(radian);
    if(r===0){
        console.warn('over range for math method:csc!');
        return MAX_FLOAT;
    }
    return 1 / r;
};

/**
 * 反余切
 * @param number
 * @returns {number}
 */
const acot=(number)=> {
    return Math.PI / 2 - Math.atan(number);
};

/**
 * 对数
 * @param base
 * @param antiLog
 * @returns {number}
 */
const log=(base,antiLog) =>{
    if (base <= 0 || base === 1) {
        console.warn('over range for math method:log!');
        return MAX_FLOAT;
    }
    if (base === antiLog)return 1;
    if (antiLog === 1)return 0;
    if (base === EULER_NUMBER)return Math.log(antiLog);
    let baseLe = Math.log(base);
    let antiLogLe = Math.log(antiLog);
    return antiLogLe / baseLe;
};

/**
 * 指数（e的x次方）
 * @param x
 * @returns {number}
 */
const exp=(x)=> {
    return Math.pow(EULER_NUMBER, x);
};

/**
 * 随机
 * @param min
 * @param max
 * @returns {*}
 */
const random=(min, max)=> {
    let delta = max - min;
    let rDelta = delta * Math.random();
    return min + rDelta;
};

/**
 * 阶乘
 * @param n
 * @returns {number}
 */
const factorial=(n)=> {
    let result = 0;
    if (n === 0) result = 1;
    if (n === 0.5) result = 0.886226925;
    if (n > 0.5 && n < 1) result = (1 + Math.sin(n * Math.PI) / (1.4 + 25 * n)) * Math.pow(n, 0.55 * n);
    if (n > 0 && n < 0.5) {
        let N = 1 - n;
        let V = factorial(N);
        result = (N * n * Math.PI) / (V * Math.sin(n * Math.PI));
    }
    if (n === 1) result = 1;
    if (n > 1 && n % 1 !== 0) {
        result = n * factorial(n - 1);
    }
    if (n > 1 && n % 1 === 0) {
        result = n * factorial(n - 1);
    }
    if (n < 0) {
        console.warn('invalid parameter for math method:factorial!');
    }
    return result;
};

//-----------------------------------------------------------------------------------------------------

/**
 *定义运算符
 */
let Operator={
    /**
     * 定义运算符的符号
     */
    item:{
        sub:'-',
        add:'+',
        mul:'*',
        exp:'/',
        mod:'%',
        psl:'(',
        psr:')',
        cma:','
    },
    /**
     * 定义运算符优先级
     */
    priority:{
        sub:0,
        add:0,
        mul:1,
        exp:1,
        mod:1,
        psl:2,
        psr:2,
        cma:-1
    },
    /**
     * 获取所有支持的运算符
     * @returns {array}
     */
    all:()=>{
        return Object.values(Operator.item);
    },
    /**
     * 检测目标是不是运算符
     * @param str
     * @returns {boolean}
     */
    isOperator:(str)=> {
        for(let key of Object.keys(Operator.item)){
            if(str===Operator.item[key])return true;
        }
        return false;
    },
    /**
     * 获取运算符的键名
     * @param str
     * @returns {string|boolean}
     */
    getItem:(str)=>{
        for(let key of Object.keys(Operator.item)){
            if(Operator.item[key]===str)return key;
        }
        return false;
    }
};

/**
 * 定义常数
 */
let Const={
    /**
     * 定义常数的符号
     */
    item:{
        pi: 'pi',
        e: 'e'
    },
    /**
     * 定义常数的值
     */
    val:{
        pi:PI,
        e:EULER_NUMBER
    },
    /**
     * 获取所有常数
     * @returns {array}
     */
    all:()=>{
        return Object.values(Const.item);
    },
    /**
     * 检测目标是不是常数
     * @param str
     * @returns {boolean}
     */
    isConst:(str)=>{
        for(let key of Object.keys(Const.item)){
            if(str===Const.item[key])return true;
        }
        return false;
    },
    /**
     * 获取常数的键名
     * @param str
     * @returns {string|boolean}
     */
    getItem:(str)=>{
        for(let key of Object.keys(Const.item)){
            if(Const.item[key]===str)return key;
        }
        return false;
    }
};

/**
 * 定义函数
 */
let Function= {
    /**
     * 定义函数名
     */
    item: {
        pow: 'power',
        sin: 'sin',
        cos: 'cos',
        tan: 'tan',
        cot: 'cot',
        abs: 'abs',
        sec: 'sec',
        csc: 'csc',
        asin: 'asin',
        acos: 'acos',
        atan: 'atan',
        acot: 'acot',
        log: 'log',
        exp: 'exp',
        rand: 'random',
        fact: 'factorial'
    },
    /**
     * 定义函数的参数数量（分隔符逗号的数量代表）
     */
    comma: {
        pow: 1,
        sin: 0,
        cos: 0,
        tan: 0,
        cot: 0,
        abs: 0,
        sec: 0,
        csc: 0,
        asin: 0,
        acos: 0,
        atan: 0,
        acot: 0,
        log: 1,
        exp: 0,
        rand: 1,
        fact: 0
    },
    /**
     * 定义函数的实现
     */
    val:{
        pow:Math.pow,
        sin:Math.sin,
        cos:Math.cos,
        tan:tan,
        cot:cot,
        abs:Math.abs,
        sec:sec,
        csc:csc,
        asin:Math.asin,
        acos:Math.acos,
        atan:Math.atan,
        acot:acot,
        log:log,
        exp:exp,
        rand:random,
        fact:factorial
    },
    /**
     * 获取所有函数
     * @returns {[]}
     */
    all:()=>{
        return Object.values(Function.item);
    },
    /**
     * 检测目标是否是函数
     * @param str
     * @returns {boolean}
     */
    isFunction: (str) => {
        for (let key of Object.keys(Function.item)) {
            if (str === Function.item[key])return true;
        }
        return false;
    },
    /**
     * 获取函数的键名
     * @param str
     * @returns {string|boolean}
     */
    getItem:(str)=>{
        for(let key of Object.keys(Function.item)){
            if(Function.item[key]===str)return key;
        }
        return false;
    }
};

/**
 * 表达式分解及判断规则
 */
let Rule= {
    /**
     * 根据定义的运算符返回正则表达式
     * @returns {RegExp}
     */
    splitRegExp:()=>{
        let s= '[';
        for (let key of Object.keys(Operator.item)) {
            s += Operator.item[key];
            s += '|';
        }
        s = s.substring(0, s.length - 1);
        s += ']';
        return new RegExp(s);
    },
    /**
     * 根据参数返回字符串在当前规则是否是不允许的
     * @param str 要检测的字符
     * @param allowNull 是否允许为空
     * @param exceptList 不允许的项中例外的项（即允许的项）
     * @param list 不允许的列表
     * @returns {boolean}
     */
    notAllowed:(str,allowNull=true,exceptList,...list)=>{
        if(str===undefined)return allowNull;
        if(str==='')return allowNull;
        if(exceptList.findIndex((s)=>s===str)!==-1)return true;
        return !(list.findIndex((s)=>s===str)!==-1);
    },
    /**
     * 定义运算符的规则（每个运算符都检测其左边和右边内容是否合法）
     */
    operatorRule: {
        /**
         * 加号左侧不能为空，且只能是右括号
         * 加号右侧不能为空，可以是左括号或其它任意非运算符
         * （其它运算符同理）
         * @param leftStr
         * @param rightStr
         * @returns {*|boolean}
         */
        add: function (leftStr, rightStr) {
            let leftLegal=Rule.notAllowed(leftStr,false,[Operator.item.psr],...Operator.all(),...Function.all());
            let rightLegal = Rule.notAllowed(rightStr,false,[Operator.item.psl],...Operator.all());
            return leftLegal && rightLegal;
        },
        sub: function (leftStr, rightStr) {
            let leftLegal = Rule.notAllowed(leftStr,true,[Operator.item.psl,Operator.item.psr,Operator.item.cma],
                ...Operator.all(),...Function.all());
            let rightLegal = Rule.notAllowed(rightStr,false,[Operator.item.psl],...Operator.all());
            return leftLegal && rightLegal;
        },
        mul: function (leftStr, rightStr) {
            let leftLegal = Rule.notAllowed(leftStr,false,[Operator.item.psr],...Operator.all(),...Function.all());
            let rightLegal = Rule.notAllowed(rightStr,false,[Operator.item.psl],...Operator.all());
            return leftLegal && rightLegal;
        },
        exp: function (leftStr, rightStr) {
            let leftLegal = Rule.notAllowed(leftStr,false,[Operator.item.psr],...Operator.all(),...Function.all());
            let rightLegal = Rule.notAllowed(rightStr,false,[Operator.item.psl],...Operator.all());
            return leftLegal && rightLegal;
        },
        mod: function (leftStr, rightStr) {
            let leftLegal = Rule.notAllowed(leftStr,false,[Operator.item.psr],...Operator.all(),...Function.all());
            let rightLegal = Rule.notAllowed(rightStr,false,[Operator.item.psl],...Operator.all());
            return leftLegal && rightLegal;
        },
        psl: function (leftStr, rightStr) {
            let leftLegal = !Rule.notAllowed(leftStr,false,[Operator.item.psr],...Operator.all(),...Function.all());
            let rightLegal = Rule.notAllowed(rightStr,false,[Operator.item.psr,Operator.item.psl,Operator.item.sub],
                ...Operator.all());
            return leftLegal && rightLegal;
        },
        psr: function (leftStr, rightStr) {
            let leftLegal = Rule.notAllowed(leftStr,false,[Operator.item.psr],...Operator.all(),...Function.all());
            let rightLegal = !Rule.notAllowed(rightStr,false,[Operator.item.psl],...Operator.all());
            return leftLegal && rightLegal;
        },
        cma: function (leftStr, rightStr) {
            let leftLegal = Rule.notAllowed(leftStr,false,[Operator.item.psr],...Operator.all(),...Function.all());
            let rightLegal = Rule.notAllowed(rightStr,false,[Operator.item.psl,Operator.item.sub],...Operator.all());
            return leftLegal && rightLegal;
        }
    },
    /**
     * 定义函数的规则
     */
    functionRule: {
        pow: function (remainArray) {
            let tempCommaNum = Function.comma.pow;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        sin: function (remainArray) {
            let tempCommaNum = Function.comma.sin;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        cos: function (remainArray) {
            let tempCommaNum = Function.comma.cos;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        tan: function (remainArray) {
            let tempCommaNum = Function.comma.tan;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        cot: function (remainArray) {
            let tempCommaNum = Function.comma.cot;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        sec: function (remainArray) {
            let tempCommaNum = Function.comma.sec;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        csc: function (remainArray) {
            let tempCommaNum = Function.comma.csc;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        abs: function (remainArray) {
            let tempCommaNum = Function.comma.abs;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        asin: function (remainArray) {
            let tempCommaNum = Function.comma.asin;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        acos: function (remainArray) {
            let tempCommaNum = Function.comma.acos;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        atan: function (remainArray) {
            let tempCommaNum = Function.comma.atan;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        acot: function (remainArray) {
            let tempCommaNum = Function.comma.acot;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        log: function (remainArray) {
            let tempCommaNum = Function.comma.log;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        exp: function (remainArray) {
            let tempCommaNum = Function.comma.exp;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        rand: function (remainArray) {
            let tempCommaNum = Function.comma.rand;
            return Rule.checkFunction(tempCommaNum, remainArray);
        },
        fact: function (remainArray) {
            let tempCommaNum = Function.comma.fact;
            return Rule.checkFunction(tempCommaNum, remainArray);
        }
    },
    /**
     * 按运算符分解字符串
     * @param str
     * @returns {[]}
     */
    stringToArray: (str) => {
        let temp = undefined;
        try {
            temp=str.split(Rule.splitRegExp());
        } catch (e) {
            if(debug)console.warn('function illegal!');
            return;
        }
        let scatter = str.split("");
        if (Operator.isOperator(scatter[0])) temp = temp.slice(1);
        let signs = [];
        for (let i of scatter) {
            if (Operator.isOperator(i)) signs.push(i);
        }
        let array = [];
        let sIndex = 0;
        let tIndex = 0;
        for (let i = 0; i < temp.length + signs.length; i++) {
            if (Operator.isOperator(scatter[0])) {
                if (i % 2 === 0) {
                    array.push(signs[sIndex]);
                    sIndex++;
                } else {
                    if (temp[tIndex] !== "") array.push(temp[tIndex]);
                    tIndex++;
                }
            } else {
                if (i % 2 === 0) {
                    if (temp[tIndex] !== "") array.push(temp[tIndex]);
                    tIndex++;
                } else {
                    array.push(signs[sIndex]);
                    sIndex++;
                }
            }
        }
        return array;
    },
    /**
     * 判断表达式是否合法
     * @param strArray
     * @param parameters
     * @returns {boolean}
     */
    isExpressionLegal: (strArray, parameters) => {
        if (strArray === undefined)return;
        let parenthesisLeftNum = 0;
        let parenthesisRightNum = 0;
        let commaNum = 0;
        let fucCommaNum = 0;
        for (let i = 0; i < strArray.length; i++) {
            if (strArray[i] === Operator.item.psl) {
                parenthesisLeftNum++;
            }
            if (strArray[i] === Operator.item.psr) {
                parenthesisRightNum++;
                if(strArray[i-1]===undefined||strArray[i-1]===Operator.item.psl)return false;
            }
            if (strArray[i] === Operator.item.cma) commaNum++;
            if (Operator.isOperator(strArray[i])) {
                if (i === 0) {
                    if(strArray.length===1)return false;
                    if (strArray[i] !== Operator.item.psl && strArray[i] !== Operator.item.sub) {
                        if(debug)console.warn('' + strArray[i] + ' is illegally using!');
                        return false;
                    }
                } else if (i === strArray.length - 1) {
                    if (strArray[i] !== Operator.item.psr) {
                        if(debug)console.warn('' + strArray[i] + ' is illegally using!');
                        return false;
                    }
                } else {
                    for (let key of Object.keys(Operator.item)) {
                        if (strArray[i] === Operator.item[key]) {
                            if (!Rule.operatorRule[key](strArray[i - 1], strArray[i + 1])) {
                                if(debug)console.warn('' + strArray[i] + ' is illegally using!');
                                return false;
                            }
                        }
                    }
                }
            } else if (Function.isFunction(strArray[i])) {
                if (i === strArray.length - 1) {
                    if(debug)console.warn('' + strArray[i] + ' is illegally using!');
                    return false;
                }
                let remainArray = strArray.slice(i + 1);
                for (let key of Object.keys(Function.item)) {
                    if (strArray[i] === Function.item[key]) {
                        if (!Rule.functionRule[key](remainArray)) {
                            if(debug)console.warn('' + strArray[i] + ' is illegally using!');
                            return false;
                        }
                        fucCommaNum += Function.comma[key];
                    }
                }
            }else if(!Const.isConst(strArray[i])){
                if (!Utils.stringIsNumber(strArray[i])) {
                    if (!Utils.inElements(strArray[i], parameters, strCompare)) {
                        if(debug)console.warn('' + strArray[i] + ' is illegally using!');
                        return false;
                    }
                }
            }
        }
        if (parenthesisLeftNum !== parenthesisRightNum) {
            if(debug)console.warn('parenthesis is illegally using!');
            return false;
        }
        if (commaNum !== fucCommaNum) {
            if(debug)console.warn('comma is illegally using!');
            return false;
        }
        return true;
    },
    /**
     * 检测内容列表对于具有给定逗号（分隔符）数量的函数是否合法
     * @param tempCommaNum 分隔符的数量（代表参数的数量）
     * @param remainArray 函数名后的内容列表
     * @returns {boolean}
     */
    checkFunction: (tempCommaNum, remainArray) => {
        /**
         * 内容开头必须是左括号
         */
        if (remainArray[0] !== Operator.item.psl)return false;
        /**
         * 检测参数数量是否正确
         */
        let tempP = 1;
        let tempC = 0;
        for (let i = 1; i < remainArray.length; i++) {
            if (remainArray[i] === Operator.item.psl) tempP++;
            if (remainArray[i] === Operator.item.psr) tempP--;
            if (Function.isFunction(remainArray[i])) {
                for (let key in Function.item) {
                    if (remainArray[i] === Function.item['' + key + '']) {
                        tempC -= Function.comma['' + key + ''];
                    }
                }
            }
            if (remainArray[i] === Operator.item.cma) tempC++;
            if (tempP === 0)break;
        }
        return (tempC === tempCommaNum);
    }
};

/**
 * 根运算符（切分计算块的运算符）
 * @type {*[]}
 */
let RootOperator=[
    Operator.item.sub,
    Operator.item.add,
    Operator.item.mul,
    Operator.item.exp,
    Operator.item.mod
];

/**
 * 用户自定义运算符号时用到的替代记录
 */
class TableElement{
    constructor(oriStr,newStr){
        this.oriStr=oriStr;
        this.newStr=newStr;
    }
}

/**
 * 可执行函数组（程序函数）
 * groups:输入的函数组
 * rootOpr:根运算符
 * rootFunc:根函数
 * uPart:参数区u
 * vPart:参数区v
 * paras:参数值
 */
class Group{
    constructor(groups,rootOpr,rootFunc,uPart,vPart,paras){
        this.paras=paras;
        this.groups=groups;
        this.rootOpr=rootOpr;
        this.rootFunc=rootFunc;
        this.uPart=uPart;
        this.vPart=vPart;
        this.type=this.checkType();
    }

    /**
     * 获取函数组类型（chain:多个函数组成的链,func:单个函数,value:值）
     * @returns {string}
     */
    checkType(){
        if(this.groups.length!==1){
            return Group.type.chain;
        }else if(this.rootFunc===undefined){
            return Group.type.value;
        }else{
            return Group.type.func;
        }
    }

    /**
     * 组合为可执行函数
     * @returns {(function(): number)|*|(function(): *)|(function(...[*]): *)}
     */
    toFunction(){
        if(this.type===Group.type.value){
            let str=this.groups[0].join("");
            let index=this.paras.findIndex((t)=>t===str);
            if(index!==-1){
                return (...paras)=>{return paras[index]};
            }else if(Const.isConst(str)){
                let key=Const.getItem(str);
                if(key!==false){
                    return ()=>{return Const.val[key]};
                }else{
                    return ()=>{return 0};
                }
            }else{
                return ()=>{return Number.parseFloat(str)};
            }
        }else if(this.type===Group.type.func){
            if(this.vPart===undefined) {
                let fu = this.uPart.toFunction();
                let key = this.rootFunc;
                return (...paras) => {
                    return Function.val[key](fu(...paras))
                };
            }else{
                let fv = this.vPart.toFunction();
                let fu=this.uPart.toFunction();
                let key = this.rootFunc;
                return (...paras)=>{
                    return Function.val[key](fu(...paras),fv(...paras))
                };
            }
        }else if(this.type===Group.type.chain){
            let fs=[];
            for(let i=0;i<this.groups.length;i++){
                let g=this.groups[i];
                let f=g.toFunction();
                fs.push(f);
            }
            let ops=this.rootOpr.slice(0);
            for(let i=0;i<this.rootOpr.length;i++){
                let so=Group.getPriorityOpr(ops);
                let pIndex=so.index;
                let nIndex=so.index+1;
                let tf=undefined;
                let _fs=fs.slice(0);
                switch(Operator.item[so.item]){
                    case Operator.item.add:
                        tf = (...paras) => {return _fs[pIndex](...paras)+_fs[nIndex](...paras)};
                        break;
                    case Operator.item.sub:
                        tf = (...paras) => {return _fs[pIndex](...paras)-_fs[nIndex](...paras)};
                        break;
                    case Operator.item.mul:
                        tf = (...paras) => {return _fs[pIndex](...paras)*_fs[nIndex](...paras)};
                        break;
                    case Operator.item.exp:
                        tf = (...paras) => {return _fs[pIndex](...paras)/_fs[nIndex](...paras)};
                        break;
                    case Operator.item.mod:
                        tf = (...paras) => {return _fs[pIndex](...paras)%_fs[nIndex](...paras)};
                        break;
                }
                if(tf!==undefined){
                    fs.splice(pIndex,2,tf);
                    ops.splice(so.index,1);
                }
            }
            return fs[0];
        }
    }

    /**
     * 获取运算的优先级
     * @param ops
     * @returns {undefined}
     */
    static getPriorityOpr(ops) {
        let pr=undefined;
        let cur=undefined;
        for (let i = 0; i < ops.length; i++) {
            let op = ops[i];
            if(pr===undefined){
                cur={item:op.item,index:i};
                pr=Operator.priority[op.item];
            }else if(Operator.priority[op.item]>pr){
                cur={item:op.item,index:i};
            }
        }
        return cur;
    }
}

/**
 * 函数组类型
 * @type {{chain: string, func: string, value: string}}
 */
Group.type={
    chain:'operatorChain',
    value:'value',
    func:'function'
};

//--------------------------------------------------------------------------------------------------------

/**
 * 单个表达式
 * paras:未知数列表，如'x','a','u'...
 */
class Expression{
    constructor(...paras){
        this._eventCenter = new EventDispatcher();
        this._parameters=Expression.getParas(paras);
        this._speedMode=0;
        this._oriString=undefined;
        this._string=undefined;
        this._stringArray=undefined;
        this._func=()=>{return 0};
        this._operatorReplaceTable = {
            sub: new TableElement('-', Operator.item.sub),
            add: new TableElement('+', Operator.item.add),
            mul: new TableElement('*', Operator.item.mul),
            exp: new TableElement('/', Operator.item.exp),
            mod: new TableElement('%', Operator.item.mod),
            psl: new TableElement('(', Operator.item.psl),
            psr: new TableElement(')', Operator.item.psr),
            cma: new TableElement(',', Operator.item.cma)
        };
        this._functionReplaceTable = {
            pow: new TableElement('power', Function.item.pow),
            sin: new TableElement('sin', Function.item.sin),
            cos: new TableElement('cos', Function.item.cos),
            tan: new TableElement('tan', Function.item.tan),
            cot: new TableElement('cot', Function.item.cot),
            abs: new TableElement('abs', Function.item.abs),
            sec: new TableElement('sec', Function.item.sec),
            csc: new TableElement('csc', Function.item.csc),
            asin: new TableElement('asin', Function.item.asin),
            acos: new TableElement('acos', Function.item.acos),
            atan: new TableElement('atan', Function.item.atan),
            acot: new TableElement('acot', Function.item.acot),
            log: new TableElement('log', Function.item.log),
            exp: new TableElement('exp', Function.item.exp),
            rand: new TableElement('random', Function.item.rand),
            fact: new TableElement('factorial', Function.item.fact)
        };
        this._constReplaceTable={
            pi: new TableElement('pi',Const.item.pi),
            e: new TableElement('e', Const.item.e)
        };
        this._errorEvent = {type: 'error', string:''};
    }

    /**
     * 设置速度模式
     * @param num
     * 0：替换用户重定义符号+检测合法性+函数化
     * 1：检测合法性+函数化
     * 2：函数化
     */
    set speedMode(num){
        this._speedMode=num;
    }
    /**
     * 开启或关闭调试
     * @param bool
     */
    static set debug(bool){
        debug=bool;
    }

    /**
     * 获取事件对象
     * @returns {EventDispatcher}
     */
    get eventCenter(){
        return this._eventCenter;
    }

    /**
     * 获取运算符置换表
     */
    get operatorReplaceTable(){
        return this._operatorReplaceTable;
    }

    /**
     * 获取函数置换表
     * @returns {{tan: *, fact: *, log: *, cos: *, cot: *, acot: *, acos: *, atan: *, sec: *, rand: *, csc: *, abs: *, pow: *, sin: *, asin: *, exp: *}|*}
     */
    get functionReplaceTable(){
        return this._functionReplaceTable;
    }

    /**
     * 获取常量置换表
     * @returns {{e: *, pi: *}|*}
     */
    get constReplaceTable(){
        return this._constReplaceTable;
    }

    /**
     * 检测字符串是否合法
     * @param string
     * @returns {boolean}
     */
    legal(string) {
        let _string = this._replaceCustomItems(string, true);
        let stringArray = Rule.stringToArray(_string);
        return Expression.checkLegally(stringArray, Object.keys(this._parameters));
    }

    /**
     * 输入字符串表达式及未知数符号
     * @param string
     * @param paras
     */
    input(string,...paras){
        if(paras.length!==0)this._parameters=Expression.getParas(paras);
        this._oriString=typeof (string)==='string'?string:'0';
        if(this._speedMode===0){
            this._string=this._replaceCustomItems(this._oriString);
            this._stringArray=Rule.stringToArray(this._string);
            if(!Expression.checkLegally(this._stringArray,Object.keys(this._parameters))){
                if(debug)console.warn('expression is illegal');
                return;
            }
        }else if(this._speedMode===1){
            this._string=this._oriString;
            this._stringArray=Rule.stringToArray(this._string);
            if(!Expression.checkLegally(this._stringArray,Object.keys(this._parameters))){
                if(debug)console.warn('expression is illegal');
                return;
            }
        }else if(this._speedMode===2){
            this._stringArray=Rule.stringToArray(this._string);
        }else{
            if(debug)console.warn('no such speedMode specified');
            return;
        }
        try {
            this._func = this._toFunction(this._stringArray);
        }catch (arg){
            if(debug)console.warn(arg);
            this._func=()=>{return 0};
        }
    }

    /**
     * 输出结果（参数值列表顺序和输入的未知数顺序一致）
     * @param values
     * @returns {number}
     */
    output(...values){
        return this._func(...values);
    }

    /**
     * 检测内容序列是否合法
     * @param array
     * @param paras
     * @returns {*|boolean}
     */
    static checkLegally(array,paras){
        return Rule.isExpressionLegal(array,Object.values(paras));
    }

    /**
     * 获取未知数对象列表
     * @param paras
     */
    static getParas(paras){
        let ps={};
        let _paras=[];
        Utils.decomposeArray(paras,_paras);
        for(let i=0;i<_paras.length;i++){
            ps[_paras[i]]={};
            ps[_paras[i]].value=undefined;
            ps[_paras[i]].index=i;
        }
        return ps;
    }

    /**
     * 检测字符串是否是未知数
     * @param str
     * @returns {boolean}
     */
    isParameters(str){
        return !(this._parameters[''+str+'']===undefined);
    }

    /**
     * 根据索引获取未知数
     * @param index
     * @returns {boolean|*}
     */
    getParaByIndex(index){
        for(let key of Object.keys(this._parameters)){
            if(this._parameters[key].index===index)return this._parameters[key];
        }
        return false;
    }

    /**
     * 根据键名获取未知数
     * @param key
     * @returns {boolean|*}
     */
    getParaValByKey(key){
        if(this._parameters[key]===undefined)return false;
        return this._parameters[key].value;
    }

    /**
     * 重定义运算符
     * @param values
     */
    setOperator(values = {}) {
        for (let key of Object.keys(values)) {
            if (!this._operatorReplaceTable.hasOwnProperty(key)) {
                if(debug)console.warn("'" + key + "'" + "is not a property of operatorReplaceTable!");
                continue;
            }
            if (!(typeof values[key] === 'string')) {
                if(debug)console.warn("'" + key + "'" + "need to be type of string!");
                continue;
            }
            this._operatorReplaceTable[key].oriStr = values[key];
        }
    }

    /**
     * 重定义函数
     * @param values
     */
    setFunction(values = {}) {
        for (let key of Object.keys(values)) {
            if (!this._functionReplaceTable.hasOwnProperty(key)) {
                if(debug)console.warn("'" + key + "'" + "is not a property of functionReplaceTable!");
                continue;
            }
            if (!(typeof values[key] === 'string')) {
                if(debug)console.warn("'" + key + "'" + "need to be type of string!");
                continue;
            }
            this._functionReplaceTable[key].oriStr = values[key];
        }
    }

    /**
     * 重定义常量
     * @param values
     */
    setConst(values={}){
        for (let key of Object.keys(values)) {
            if (!this._constReplaceTable.hasOwnProperty(key)) {
                if(debug)console.warn("'" + key + "'" + "is not a property of constReplaceTable!");
                continue;
            }
            if (!(typeof values[key] === 'string')) {
                if(debug)console.warn("'" + key + "'" + "need to be type of string!");
                continue;
            }
            this._constReplaceTable[key].oriStr = values[key];
        }
    }

    /**
     * 从其它表达式对象设置运算符，函数及常量
     * @param parametricFunction
     */
    setOprFuncCnstFrom(parametricFunction){
        for(let key of Object.keys(this._operatorReplaceTable)){
            this._operatorReplaceTable[key].oriStr=parametricFunction._operatorReplaceTable[key].oriStr;
        }
        for(let key of Object.keys(this._functionReplaceTable)){
            this._functionReplaceTable[key].oriStr=parametricFunction._functionReplaceTable[key].oriStr;
        }
        for(let key of Object.keys(this._constReplaceTable)){
            this._constReplaceTable[key].oriStr=parametricFunction._constReplaceTable[key].oriStr;
        }
    }

    _isOperatorReplaceElement(str) {
        for (let key of Object.keys(this._operatorReplaceTable)) {
            if (str === this._operatorReplaceTable[key].oriStr)return true;
        }
        return false;
    }
    _isFunctionReplaceElement(str) {
        for (let key of Object.keys(this._functionReplaceTable)) {
            if (str === this._functionReplaceTable[key].oriStr)return true;
        }
        return false;
    }
    _isConstReplaceElement(str) {
        for (let key of Object.keys(this._constReplaceTable)) {
            if (str === this._constReplaceTable[key].oriStr)return true;
        }
        return false;
    }
    _expressionStringToArray(str) {
        let rEO = () => {
            let s = '[';
            for (let key of Object.keys(this._operatorReplaceTable)) {
                s += this._operatorReplaceTable[key].oriStr;
                s += '|';
            }
            s = s.substring(0, s.length - 1);
            s += ']';
            return new RegExp(s);
        };
        let temp = str.split(rEO());
        let scatter = str.split("");
        if (this._isOperatorReplaceElement(scatter[0])) temp = temp.slice(1);
        let signs = [];
        for (let i of scatter) {
            if (this._isOperatorReplaceElement(i)) signs.push(i);
        }
        let array = [];
        let sIndex = 0;
        let tIndex = 0;
        for (let i = 0; i < temp.length + signs.length; i++) {
            if (this._isOperatorReplaceElement(scatter[0])) {
                if (i % 2 === 0) {
                    array.push(signs[sIndex]);
                    sIndex++;
                } else {
                    if (temp[tIndex] !== "") array.push(temp[tIndex]);
                    tIndex++;
                }
            } else {
                if (i % 2 === 0) {
                    if (temp[tIndex] !== "") array.push(temp[tIndex]);
                    tIndex++;
                } else {
                    array.push(signs[sIndex]);
                    sIndex++;
                }
            }
        }
        return array;
    }
    _replaceCustomItems(str,onlyCheck=false){
        if(onlyCheck){
            if (str === '' || str === undefined) return null;
            let rArray = this._expressionStringToArray(str);
            for (let i = 0; i < rArray.length; i++) {
                if (this._isOperatorReplaceElement(rArray[i])) {
                    for (let key of Object.keys(this._operatorReplaceTable)) {
                        if (rArray[i] === this._operatorReplaceTable[key].oriStr) rArray[i] = this._operatorReplaceTable[key].newStr;
                    }
                } else if (this._isFunctionReplaceElement(rArray[i])) {
                    for (let key of Object.keys(this._functionReplaceTable)) {
                        if (rArray[i] === this._functionReplaceTable[key].oriStr) rArray[i] = this._functionReplaceTable[key].newStr;
                    }
                }else if(this._isConstReplaceElement(rArray[i])){
                    for (let key of Object.keys(this._constReplaceTable)) {
                        if (rArray[i] === this._constReplaceTable[key].oriStr) rArray[i] = this._constReplaceTable[key].newStr;
                    }
                }else{
                    if (!Utils.stringIsNumber(rArray[i])) {
                        if (!this.isParameters(rArray[i])) {
                            return null;
                        }
                    }
                }
            }
            return rArray.join("");
        }
        if (str === '' || str === undefined) str = '0';
        let rArray = this._expressionStringToArray(str);
        for (let i = 0; i < rArray.length; i++) {
            if (this._isOperatorReplaceElement(rArray[i])) {
                for (let key of Object.keys(this._operatorReplaceTable)) {
                    if (rArray[i] === this._operatorReplaceTable[key].oriStr) rArray[i] = this._operatorReplaceTable[key].newStr;
                }
            } else if (this._isFunctionReplaceElement(rArray[i])) {
                for (let key of Object.keys(this._functionReplaceTable)) {
                    if (rArray[i] === this._functionReplaceTable[key].oriStr) rArray[i] = this._functionReplaceTable[key].newStr;
                }
            }else if(this._isConstReplaceElement(rArray[i])){
                for (let key of Object.keys(this._constReplaceTable)) {
                    if (rArray[i] === this._constReplaceTable[key].oriStr) rArray[i] = this._constReplaceTable[key].newStr;
                }
            } else {
                // let reg = /^[0-9]+.?[0-9]*$/;
                // if (!reg.test(rArray[i])) {
                if (!Utils.stringIsNumber(rArray[i])) {
                    if (!this.isParameters(rArray[i])) {
                        this._errorEvent.string = "Parameter expression is illegal";
                        this._eventCenter.dispatchEvent(this._errorEvent);
                        return '0';
                    }
                }
            }
        }
        return rArray.join("");
    }
    _toFunction(_array){
        let array=this._addZero(_array);
        let group=Expression.resolute(array,Object.keys(this._parameters));
        Expression.recursiveGroup(group,Object.keys(this._parameters));
        return group.toFunction();
    }
    _addZero(ary){
        let temp=[];
        let signClass=[Operator.item.cma,Operator.item.psl];
        for(let i=0;i<ary.length;i++){
            if(ary[i]===Operator.item.sub){
                let isSign=(ary[i-1]===undefined)||(signClass.findIndex((s)=>s===ary[i-1])!==-1);
                if(isSign){
                    temp.push('0',ary[i]);
                    continue;
                }
            }
            temp.push(ary[i]);
        }
        return temp;
    }
    static resolute(array,paras){
        let rootOpr=Expression.getRootOperator(array);
        let groups=Expression.groupByRootOperator(array,rootOpr);
        let rootFunc=undefined;
        let uPart=undefined;
        let vPart=undefined;
        if(groups.length===1){
            if(groups[0].length!==1&&groups[0][0]!==Operator.item.sub){
                rootFunc=Expression.getRootFunction(groups[0]);
                if(Function.comma[rootFunc]===0) {
                    uPart = Expression.getUPart(groups[0]);
                }else if(Function.comma[rootFunc]===1) {
                    uPart = Expression.getUPart(groups[0]);
                    vPart = Expression.getVPart(groups[0]);
                }else{
                    if(debug)console.warn('the comma of this function is undefined!');
                }
            }
        }
        return new Group(groups,rootOpr,rootFunc,uPart,vPart,paras);
    }
    static recursiveGroup(group,paras){
        if(group.type===Group.type.value)return;
        if(group.type===Group.type.chain){
            for(let i=0;i<group.groups.length;i++){
                group.groups[i]=Expression.resolute(group.groups[i],paras);
                Expression.recursiveGroup(group.groups[i],paras);
            }
        }else if(group.type===Group.type.func){
            group.uPart=Expression.resolute(group.uPart,paras);
            Expression.recursiveGroup(group.uPart,paras);
            if(group.vPart!==undefined) {
                group.vPart = Expression.resolute(group.vPart,paras);
                Expression.recursiveGroup(group.vPart,paras);
            }
        }
    }
    static removeUselessParentheses(array){
        let tempArray = undefined;
        let pCount = 0;
        if (array[0] !== Operator.item.psl) {
            tempArray = array.slice(0);
        } else if (array[array.length - 1] !== Operator.item.psr) {
            tempArray = array.slice(0);
        } else {
            let remove=(ary)=>{
                if(ary.length===1)return ary;
                for (let i = 0; i < ary.length; i++) {
                    if (ary[i] === Operator.item.psl) pCount++;
                    if (ary[i] === Operator.item.psr) pCount--;
                    if (pCount === 0) {
                        if (i !== ary.length - 1) {
                            return ary.slice(0);
                        } else {
                            pCount=0;
                            let tempAry = ary.slice(1, ary.length - 1);
                            return remove(tempAry);
                        }
                    }
                }
            };
            tempArray=remove(array);
        }
        return tempArray;
    }
    static getUPart(array){
        let _array=Expression.removeUselessParentheses(array);
        let pCount=0;
        let cIndex=0;
        for(let i=0;i<_array.length;i++){
            let str=_array[i];
            if(Operator.item.psl===str){
                pCount++;
            }else if(Operator.item.psr===str){
                pCount--;
                if(pCount===0){
                    cIndex=i;
                    break;
                }
            }else if(Operator.item.cma===str){
                if(pCount===1){
                    cIndex=i;
                    break;
                }
            }
        }
        return Expression.removeUselessParentheses(_array.slice(2,cIndex));
    }
    static getVPart(array){
        let _array=Expression.removeUselessParentheses(array);
        let pCount=0;
        let cIndex=0;
        for(let i=0;i<_array.length;i++){
            let str=_array[i];
            if(Operator.item.psl===str){
                pCount++;
            }else if(Operator.item.psr===str){
                pCount--;
            }else if(Operator.item.cma===str){
                if(pCount===1){
                    cIndex=i;
                    break;
                }
            }
        }
        return Expression.removeUselessParentheses(_array.slice(cIndex+1,_array.length-1));
    }
    static getRootFunction(array){
        let _array=Expression.removeUselessParentheses(array);
        let str=_array[0];
        return Function.getItem(str);
    }
    static groupByRootOperator(array,ros){
        let _array=Expression.removeUselessParentheses(array);
        let temp=[];
        if(ros.length>0) {
            for (let i = 0; i < ros.length; i++) {
                let tp = [];
                if (i === 0) {
                    tp = _array.slice(0, ros[i].index);
                    temp.push(Expression.removeUselessParentheses(tp));
                } else {
                    tp = _array.slice(ros[i - 1].index + 1, ros[i].index);
                    temp.push(Expression.removeUselessParentheses(tp));
                }
            }
            temp.push(Expression.removeUselessParentheses(_array.slice(ros[ros.length - 1].index + 1)));
        }else{
            temp=[_array.slice(0)];
        }
        return temp;
    }
    static getRootOperator(array){
        let _array=Expression.removeUselessParentheses(array);
        let ros=[];
        let pCount=0;
        for(let i=0;i<_array.length;i++){
            let str=_array[i];
            if(Operator.isOperator(str)){
                if(str===Operator.item.psl){
                    pCount++;
                }else if(str===Operator.item.psr){
                    pCount--;
                }else{
                    if(pCount===0&&i!==0){
                        if(Utils.inElements(str,RootOperator,strCompare)){
                            ros.push({item:Operator.getItem(str),index:i});
                        }
                    }
                }
            }
        }
        return ros;
    }
}

/**
 * XYZ表达式
 * @param speedMode:  0：替换用户重定义符号+检测合法性+函数化，1：检测合法性+函数化，2：函数化
 * @param paras:未知数列表
 */
class XYZExpression{
    constructor(speedMode=0,...paras){
        this._parameters=paras;
        this.fx=undefined;
        this.fy=undefined;
        this.fz=undefined;
        this._speedMode=(speedMode===0||speedMode===1)?speedMode:2;
        this._init();
    }
    _init(){
        this.fx=new Expression(this._parameters);
        this.fx.speedMode=this._speedMode;
        this.fy=new Expression(this._parameters);
        this.fy.speedMode=this._speedMode;
        this.fz=new Expression(this._parameters);
        this.fz.speedMode=this._speedMode;
    }

    /**
     * 用字符串表达式设置
     * @param eXString
     * @param eYString
     * @param eZString
     * @param paras
     */
    setupFromString(eXString,eYString,eZString,...paras) {
        if(paras.length!==0)this._parameters = paras;
        this.fx.input(eXString, this._parameters);
        this.fy.input(eYString, this._parameters);
        this.fz.input(eZString, this._parameters);
    }

    /**
     * 用表达式对象设置
     * @param eX
     * @param eY
     * @param eZ
     * @param paras
     */
    setupFromEquation(eX,eY,eZ,...paras) {
        if(paras.length!==0)this._parameters = paras;
        this.fx._parameters=Expression.getParas(this._parameters);
        this.fx = eX;
        this.fy._parameters=Expression.getParas(this._parameters);
        this.fy = eY;
        this.fz._parameters=Expression.getParas(this._parameters);
        this.fz = eZ;
    }

    /**
     * 输出结果
     * @param vectorOut  结果
     * @param parameterValues 未知数值列表
     */
    output(vectorOut,...parameterValues) {
        vectorOut.x = this.fx.output(...parameterValues);
        vectorOut.y = this.fy.output(...parameterValues);
        vectorOut.z = this.fz.output(...parameterValues);
    }
}

export default {
    E:Expression,
    XYZ:XYZExpression
};